package com.gitee.wsl.collections.bag.api

/**
 * Defines a collection that counts the number of times an object appears in
 * the collection.
 *
 *
 * Suppose you have a Bag that contains `{a, a, b, c}`.
 * Calling [.getCount] on `a` would return 2, while
 * calling [.uniqueSet] would return `{a, b, c}`.
 *
 *
 *
 * *NOTE: This interface violates the [Collection] contract.*
 * The behavior specified in many of these methods is *not* the same
 * as the behavior specified by `Collection`.
 * The non-compliant methods are clearly marked with "(Violation)".
 * Exercise caution when using a bag as a `Collection`.
 *
 *
 *
 * This violation resulted from the original specification of this interface.
 * In an ideal world, the interface would be changed to fix the problems, however
 * it has been decided to maintain backwards compatibility instead.
 *
 *
 * @param <E> the type of elements in this bag
 * @since 2.0
</E> */
interface Bag<E> : MutableCollection<E> {
    override fun isEmpty(): Boolean  = size == 0

    /**
     * *(Violation)*
     * Adds one copy of the specified object to the Bag.
     *
     *
     * If the object is already in the [.uniqueSet] then increment its
     * count as reported by [.getCount]. Otherwise add it to the
     * [.uniqueSet] and report its count as 1.
     *
     *
     *
     * Since this method always increases the size of the bag,
     * according to the [Collection.add] contract, it
     * should always return `true`.  Since it sometimes returns
     * `false`, this method violates the contract.
     *
     *
     * @param element  the object to add
     * @return `true` if the object was not already in the `uniqueSet`
     */
     //override fun add(element: E): Boolean

    /**
     * Adds `nCopies` copies of the specified object to the Bag.
     *
     *
     * If the object is already in the [.uniqueSet] then increment its
     * count as reported by [.getCount]. Otherwise add it to the
     * [.uniqueSet] and report its count as `nCopies`.
     *
     *
     * @param object  the object to add
     * @param nCopies  the number of copies to add
     * @return `true` if the object was not already in the `uniqueSet`
     * @throws ClassCastException if the class of the specified element prevents it from being added to this collection
     */
    fun add(element: E, nCopies: Int): Boolean

    /**
     * *(Violation)*
     * Returns `true` if the bag contains all elements in
     * the given collection, respecting cardinality.  That is, if the
     * given collection `coll` contains `n` copies
     * of a given object, calling [.getCount] on that object must
     * be `&gt;= n` for all `n` in `coll`.
     *
     *
     *
     * The [Collection.containsAll] method specifies
     * that cardinality should *not* be respected; this method should
     * return true if the bag contains at least one of every object contained
     * in the given collection.
     *
     *
     * @param elements  the collection to check against
     * @return `true` if the Bag contains all the collection
     */
     //override fun containsAll(elements: Collection<E>): Boolean

    /**
     * Returns the number of occurrences (cardinality) of the given
     * object currently in the bag. If the object does not exist in the
     * bag, return 0.
     *
     * @param object  the object to search for
     * @return the number of occurrences of the object, zero if not found
     */
    fun getCount(element: E): Int

    /**
     * Returns an [Iterator] over the entire set of members,
     * including copies due to cardinality. This iterator is fail-fast
     * and will not tolerate concurrent modifications.
     *
     * @return iterator over all elements in the Bag
     */
     //override fun iterator(): MutableIterator<E>

    /**
     * *(Violation)*
     * Removes all occurrences of the given object from the bag.
     *
     *
     * This will also remove the object from the [.uniqueSet].
     *
     *
     *
     * According to the [Collection.remove] method,
     * this method should only remove the *first* occurrence of the
     * given object, not *all* occurrences.
     *
     *
     * @param element  the object to remove
     * @return `true` if this call changed the collection
     */
     //override fun remove(element: E): Boolean

    /**
     * Removes `nCopies` copies of the specified object from the Bag.
     *
     *
     * If the number of copies to remove is greater than the actual number of
     * copies in the Bag, no error is thrown.
     *
     *
     * @param object  the object to remove
     * @param nCopies  the number of copies to remove
     * @return `true` if this call changed the collection
     */
    fun remove(element: E, nCopies: Int): Boolean

    /**
     * *(Violation)*
     * Remove all elements represented in the given collection,
     * respecting cardinality.  That is, if the given collection
     * `coll` contains `n` copies of a given object,
     * the bag will have `n` fewer copies, assuming the bag
     * had at least `n` copies to begin with.
     *
     *
     *
     * The [Collection.removeAll] method specifies
     * that cardinality should *not* be respected; this method should
     * remove *all* occurrences of every object contained in the
     * given collection.
     *
     *
     * @param elements  the collection to remove
     * @return `true` if this call changed the collection
     */
    // override fun removeAll(elements: Collection<E>): Boolean

    /**
     * *(Violation)*
     * Remove any members of the bag that are not in the given
     * collection, respecting cardinality.  That is, if the given
     * collection `coll` contains `n` copies of a
     * given object and the bag has `m &gt; n` copies, then
     * delete `m - n` copies from the bag.  In addition, if
     * `e` is an object in the bag but
     * `!coll.contains(e)`, then remove `e` and any
     * of its copies.
     *
     *
     *
     * The [Collection.retainAll] method specifies
     * that cardinality should *not* be respected; this method should
     * keep *all* occurrences of every object contained in the
     * given collection.
     *
     *
     * @param elements  the collection to retain
     * @return `true` if this call changed the collection
     */
     //override fun retainAll(elements: Collection<E>): Boolean

    /**
     * Returns the total number of items in the bag across all types.
     *
     * @return the total size of the Bag
     */
    // override fun size(): Int
    //override val size: Int

    /**
     * Returns a [Set] of unique elements in the Bag.
     *
     *
     * Uniqueness constraints are the same as those in [java.util.Set].
     *
     *
     * @return the Set of unique Bag elements
     */
    fun uniqueSet(): Set<E> // The following is not part of the formal Bag interface, however where possible

    fun getCountOfAll(): Int = getCount(uniqueSet())

    companion object
}


fun<E> Bag<E>.getCount(elements: Collection<E>): Int  {
    var count = 0
    elements.forEach {
        count+=getCount(it)
    }
    return count
}
